/**
 * 
 */
package soa.security.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;

import org.apache.commons.lang3.StringUtils;
import org.ldaptive.pool.BlockingConnectionPool;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.query.ContainerCriteria;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.query.SearchScope;
import org.springframework.ldap.support.LdapNameBuilder;
import org.springframework.ldap.support.LdapUtils;

import soa.security.Constants;
import soa.security.dao.SpringLdapDao;
import soa.security.domain.Organization;
import soa.security.query.Convertor;
import soa.security.query.DefaultConvertor;

/**
 * @author Cre.Gu
 *
 */
public abstract class SpringLdapDaoImpl<T> implements SpringLdapDao<T> {

	@Resource
	protected LdapTemplate ldapTemplate;
	@Resource
	protected LdaptiveSupport ldaptiveSupport;
	@Resource
	protected BlockingConnectionPool pool;

	@Override
	public void create(T t) {
		ldapTemplate.create(t);
	}

	@Override
	public void update(T t) {
		ldapTemplate.update(t);
	}

	@Override
	public void deleteByDn(String dn) {
		ldapTemplate.unbind(dn);
	}

	@Override
	public T findByPrimaryKey(String baseDn, String id) {
		try {
			ContainerCriteria query = baseQuery(baseDn, SearchScope.ONELEVEL);
			query.and("bid").is(id);

			return ldapTemplate.searchForObject(query, new BaseContextMapper());
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public List<T> findAllByAttr(String baseDn, SearchScope searchScope,
			String attrName, String attrValue) {
		ContainerCriteria query = baseQuery(baseDn, searchScope);
		query.and(attrName).is(attrValue);

		return ldapTemplate.search(query, new BaseContextMapper());
	}

	public void addAttrName(Name dn, String attrName, String attrValue) {
		DirContextOperations context = ldapTemplate.lookupContext(dn);

		context.addAttributeValue(attrName, attrValue);
		ldapTemplate.modifyAttributes(context);
	}

	public void removeAttrName(Name dn, String attrName, String attrValue) {
		DirContextOperations context = ldapTemplate.lookupContext(dn);

		context.removeAttributeValue(attrName, attrValue);
		ldapTemplate.modifyAttributes(context);
	}

	public List<String> allAttrName(Name dn, String attrName) {
		List<String> list = new ArrayList<String>();
		try {
			DirContextOperations context = ldapTemplate.lookupContext(dn);

			Attribute at = context.getAttributes().get(attrName);
			if (at == null)
				return list;
			for (int i = 0; i < at.size(); i++) {
				String dn2 = (String) at.get(i);
				list.add(dn2);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	protected class BaseContextMapper implements ContextMapper<T> {
		@Override
		public T mapFromContext(Object ctx) throws NamingException {
			DirContextAdapter context = (DirContextAdapter) ctx;
			return findByDn(context.getDn().toString());
		}
	}

	protected ContainerCriteria baseQuery(String baseDn, SearchScope searchScope) {
		LdapQueryBuilder queryBuilder = LdapQueryBuilder.query();
		if (StringUtils.isNotBlank(baseDn))
			queryBuilder.base(baseDn);

		LdapQueryBuilder query = queryBuilder.searchScope(searchScope);

		ContainerCriteria result = query.where("objectclass")
				.is(getObjectClass()).and(Constants.DELETED).not().is("TRUE");

		return result;
	}

	protected void buildEqualsCriteria(ContainerCriteria cc,
			Map<String, Object> params, String key, Convertor convertor) {
		if (params == null)
			return;

		String[] keys = (String[]) params.get(key);
		if (keys != null && keys.length > 0) {
			if (StringUtils.isNotBlank(keys[0])) {
				String con = convertor.convert(keys[0]);
				cc.and(key).is(con);
			}
		}
	}

	protected void buildEqualsCriteria(ContainerCriteria cc,
			Map<String, Object> params, String key) {
		buildEqualsCriteria(cc, params, key, new DefaultConvertor());
	}

	protected void buildLikeCriteria(ContainerCriteria cc,
			Map<String, Object> params, String key) {
		if (params == null)
			return;

		String[] keys = (String[]) params.get(key);
		if (keys != null && keys.length > 0) {
			if (StringUtils.isNotBlank(keys[0]))
				cc.and(key).like("*" + keys[0] + "*");
		}
	}

	/**
	 * ldap机构下的分组查询机构
	 * 
	 * @param dn
	 * @return Organization
	 * 
	 * @author Cre.Gu at 2015年6月4日
	 */
	protected Organization unitBelongs(String dn) {
		String orDn = StringUtils.substringAfter(
				StringUtils.substringAfter(dn, ","), ",");

		return findOrganizationByDn(orDn);
	}

	protected Organization findOrganizationByDn(String dn) {
		try {
			Organization o = ldapTemplate
					.findByDn(LdapNameBuilder.newInstance(dn).build(),
							Organization.class);
			o.setBid(LdapUtils.getStringValue(o.getDn(), o.getDn().size() - 1));
			return o;
		} catch (Exception e) {
			return null;
		}
	}

}
